package org.dregs.repository;

import org.dregs.exception.ParamIsNullException;
import org.dregs.pojo.DataBean;
import org.dregs.pojo.Version;
import org.dregs.pojo.tuple.Tuple;
import org.dregs.pojo.tuple.Tuple2;
import org.dregs.pojo.tuple.Tuple3;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.dao.support.DataAccessUtils;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.jdbc.core.RowCallbackHandler;
import org.springframework.jdbc.core.RowMapper;

import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public abstract class DataMapper/**<T extends DataBean>*/ {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    protected JdbcTemplate getJdbcTemplate(){
        return jdbcTemplate;
    };

    protected <T> List<T> queryAll(String table, RowMapper<T> rowMapper){
        String sql = "SELECT * FROM "+table;
        return jdbcTemplate.query(sql,rowMapper);
    };

    protected <T> List<T> queryList(String sql, RowMapper<T> rowMapper){
        return jdbcTemplate.query(sql,rowMapper);
    };
    protected <T> List<T> queryList(String sql, Object[] params,RowMapper<T> rowMapper){
        return jdbcTemplate.query(sql,params,rowMapper);
    };

    protected <T> T queryObject(String sql, Class<T> tClass){
        return jdbcTemplate.queryForObject(sql,tClass);
    };
    protected <T> T queryObject(String sql, Object[] params, Class<T> tClass){
        return jdbcTemplate.queryForObject(sql,params,tClass);
    };

    protected <T> T queryObject(String sql,RowMapper<T> rowMapper){
        return DataAccessUtils.uniqueResult(queryList(sql, rowMapper));
    };
    protected <T> T queryObject(String sql, Object[] params,RowMapper<T> rowMapper){
        return DataAccessUtils.uniqueResult(queryList(sql, params, rowMapper));
    };

    protected Map<String,Object> queryMap(String sql){
        return jdbcTemplate.queryForMap(sql);
    };
    protected Map<String,Object> queryMap(String sql, Object[] params){
        return jdbcTemplate.queryForMap(sql,params);
    };

    protected long queryTableCount(String table,Object id) throws ParamIsNullException {
        String sql = "SELECT COUNT(1) as count FROM %s";
        return getOnly(getJdbcTemplate().query(String.format(sql,table),(resultSet,i)->{return resultSet.getLong("count");}));
    }

    protected int deleteRecord(String table,Object id) throws ParamIsNullException {
        if(null == id)
            throw new ParamIsNullException();
        String sql = String.format("DELETE FROM %s WHERE `id` = ?",table);
        return update(sql,id);
    }

    protected int deleteRecord(String table,DataBean dataBean) throws ParamIsNullException {
        return deleteRecord(table,dataBean.toMap());
    }

    protected int insertRecord(String table,DataBean dataBean) throws ParamIsNullException {
        return insertRecord(table,dataBean.toMap());
    }

    protected int updateRecord(String table,DataBean dataBean) throws ParamIsNullException {
        return updateRecord(table,dataBean.toMap());
    }

    protected int insertRecord(String table,Object[] params){
        String sql = String.format("INSERT INTO %s VALUES(%s)",table,placeholder(params.length));
        return update(sql,params);
    }
    protected int insertRecord(String table,Map<String,Object> map) throws ParamIsNullException {
        Tuple2<String, Object[]> tuple = parameterKV(map);
        String sql = String.format("INSERT INTO %s(%s) VALUES(%s)",table,tuple._1(),placeholder(tuple._2().length));
        return update(sql,tuple._2());
    }

    protected int updateRecord(String table,Map<String,Object> sets,Map<String,Object> wheres) throws ParamIsNullException {
        Tuple3<String, String, Object[]> tuple3 = setAndWhere(sets, wheres);
        String sql = String.format("UPDATE %s SET %s WHERE %s",table,tuple3._1(),tuple3._2());
        return update(sql,tuple3._3());
    }

    protected int updateRecord(String table,Map<String,Object> sets) throws ParamIsNullException {
        Tuple2<String,Object[]> tuple2 = set(sets);
        String sql = String.format("UPDATE %s SET %s WHERE `id` = ?",table,tuple2._1());
        return update(sql,tuple2._2());
    }

    protected int deleteRecord(String table,Map<String,Object> wheres) throws ParamIsNullException {
        Tuple2<String, Object[]> tuple2 = where(wheres);
        String sql = String.format("DELETE FROM %s WHERE %s",table,tuple2._1());
        return update(sql,tuple2._2());
    }

    protected int update(String sql){
        return jdbcTemplate.update(sql);
    }

    protected int update(String sql,Object... params){
        return jdbcTemplate.update(sql,params);
    }

    private String placeholder(int size){
        StringBuilder builder = new StringBuilder();
        for(int $i=0;$i<size;$i++){
            builder.append("?");
            if(size != ($i+1))
                builder.append(",");
        }
        return builder.toString();
    }
    private Tuple2<String,Object[]> parameterKV(Map<String,Object> map) throws ParamIsNullException {
        if(0 == map.size())
            throw new ParamIsNullException();
        ArrayList<Object> objects = new ArrayList<>();
        StringBuilder builder = new StringBuilder();
        int $i = 0;
        for(String key : map.keySet()){
            builder.append(key);
            objects.add(map.get(key));
            if(map.size() != (++$i))
                builder.append(",");
        }
        return Tuple.initialize(builder.toString(),objects.toArray());
    }
    private Tuple3<String,String,Object[]> setAndWhere(Map<String,Object> sets, Map<String,Object> wheres) throws ParamIsNullException {
        if(0 == sets.size() || 0 == wheres.size())
            throw new ParamIsNullException();
        ArrayList<Object> objects = new ArrayList<>();
        StringBuilder set = new StringBuilder();
        StringBuilder where = new StringBuilder();
        int $i = 0;
        for(String key : sets.keySet()){
            set.append(String.format("%s=?",key));
            objects.add(sets.get(key));
            if(sets.size() != (++$i))
                set.append(",");
        }
        $i = 0;
        for(String key : wheres.keySet()){
            where.append(String.format("%s=?",key));
            objects.add(wheres.get(key));
            if(wheres.size() != (++$i))
                where.append(" AND ");
        }
        return Tuple.initialize(set.toString(),where.toString(),objects.toArray());
    }

    private Tuple2<String,Object[]> where(Map<String,Object> wheres) throws ParamIsNullException {
        if(0 == wheres.size())
            throw new ParamIsNullException();
        ArrayList<Object> objects = new ArrayList<>();
        StringBuilder where = new StringBuilder();
        int $i = 0;
        for(String key : wheres.keySet()){
            where.append(String.format("%s=?",key));
            objects.add(wheres.get(key));
            if(wheres.size() != (++$i))
                where.append(" AND ");
        }
        return Tuple.initialize(where.toString(),objects.toArray());
    }

    private Tuple2<String,Object[]> set(Map<String,Object> sets) throws ParamIsNullException {
        if(0 == sets.size())
            throw new ParamIsNullException();
        Object id = sets.get("`id`");
        sets.remove("`id`");
        ArrayList<Object> objects = new ArrayList<>();
        StringBuilder where = new StringBuilder();
        int $i = 0;
        for(String key : sets.keySet()){
            where.append(String.format("%s = ?",key));
            objects.add(sets.get(key));
            if(sets.size() != (++$i))
                where.append(",");
        }
        objects.add(id);
        return Tuple.initialize(where.toString(),objects.toArray());
    }

    private <T> T getOnly(List<T> list){
        if(null == list || 0 == list.size())
            return null;
        return list.get(0);
    }


}
